package com.lnsoft.dqfs.server.handle.threadpools;

import com.lnsoft.dqfs.server.dao.ConnectArg;
import com.lnsoft.dqfs.server.dao.DqfsDao;
import com.lnsoft.dqfs.server.dao.Yc;
import com.lnsoft.dqfs.server.handle.codec.*;
import com.lnsoft.dqfs.server.util.Arrays16;
import com.lnsoft.dqfs.server.util.MessageUtil;
import com.lnsoft.dqfs.server.util.UpgradePackageCache;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Component
@Slf4j
/**
 *  业务数据处理线程池
 */
public class ServiceInvokePools {

    @Autowired
    private DqfsDao dqfsDao;
    @Autowired
    private UpgradePackageCache upgradePackageCache;

    @Value("${updata.path}")
    private String dataPath;

    @Value("${project.is_test}")
    private boolean isTest;


    private final SleepReadArgCommon sleepReadArgCommon = SleepReadArgCommon.getInstance();


    private final ExecutorService pools = Executors.newFixedThreadPool(20);

    private final ExecutorService dataPools = Executors.newFixedThreadPool(10);


    public void clearPools() {
        dataPools.shutdown();
        pools.shutdown();
    }


    public void execute(final ChannelHandlerContext ctx, final ServiceFrame seviceFrame, final Frame frame) {
        pools.execute(new Runnable() {
            @Override
            public void run() {
                int clientName = frame.getClientName();
                int port = frame.getServerPort();


                // 终端请求连接
                if (seviceFrame instanceof ConnectServiceFrame) {
//                    ConnectServiceFrame connectServiceFrame = (ConnectServiceFrame) seviceFrame;


                    String clientId = null;
                    try {
                        clientId = dqfsDao.getIacmId(clientName, port);
                    } catch (Exception e) {
                        log.error("客户端{}:{},验证身份时发生error!!", clientName, port, e);
                    }

                    if (isTest) { // 测试环境 不对终端进行验证
                        if (clientId == null) {
                            clientId = UUID.randomUUID().toString();
                        }
                    }
                    if (clientId == null) {
                        byte[] unConfimFrame = MessageUtil.createUnConfirmFrame(frame.getA1(), frame.getA2());
                        ctx.writeAndFlush(unConfimFrame);
                        log.debug("客户端{}:{},验证失败,返回报文并关闭连接：{}", clientName, port, Arrays16.toString(unConfimFrame));
                        ctx.channel().close();
                        return;
                    }

//                    Channel channel = ctx.channel();
                    // 初始化结束帧后 加入缓存
                    //  channel.attr(AttributeKey.valueOf("clientId")).set(clientId);

                    //  设置终端的数据库对应的id
                    frame.setClientId(clientId);

                    //  构造确认报文并发送
                    byte[] bytes = MessageUtil.createConfirmFrame(frame.getA1(), frame.getA2());

                    //  主站回复确认报文
                    ctx.writeAndFlush(bytes);
                    //  TODO 终端主发切换到主站主发模式，是否要预留时间间隙
                    log.debug("客户端{}:{},注册认证通过，clientId:{}，并返回确认报文：{}", clientName, port, clientId, Arrays16.toString(bytes));

                    //  构造请求报文:请求链路状态
                    byte[] connectBytes = Arrays.copyOf(bytes, 6);
                    connectBytes[1] = 73;
                    connectBytes[4] = (byte) MessageUtil.createCS(connectBytes, 1, 4);

                    //  主站发送请求报文
                    ctx.writeAndFlush(connectBytes);
                    //  设置主站下发状态:使用完整控制域的十进制数值73(fc=9请求链路状态)
                    frame.setProcess(73);
                    //  设置主站的下发FCB反转位
                    frame.setUpServerFcb(true);
                    log.debug("客户端{}:{},主站请求报文<请求链路状态>：{}", clientName, port, Arrays16.toString(connectBytes));

                    //  备份发送的请求报文
                    frame.setSendingFrame(connectBytes);
                } else if (seviceFrame instanceof ResetLinkServiceFrame) {
                    //  发送组装好的复位远方链路报文
                    ResetLinkServiceFrame resetLinkServiceFrame = (ResetLinkServiceFrame) seviceFrame;
                    byte[] bytes = resetLinkServiceFrame.getRespBytes();
                    ctx.writeAndFlush(bytes);
                    log.debug("客户端{}:{},主站请求报文<复位远方链路>：{}", clientName, port, Arrays16.toString(bytes));

                    //  备份发送的请求报文
                    frame.setSendingFrame(bytes);
                    //  设置主站的下发状态
                    frame.setProcess(64);
                    frame.setUpServerFcb(true);

                } else if (seviceFrame instanceof TimeSyncServiceFrame) {
                    // 终端时钟同步
                    // 时针同步 C= 0 1 fcb 1 3(4位)
                    byte c = MessageUtil.createServerC(frame, (byte) 1, (byte) 3);
                    byte[] time = MessageUtil.createTimeNow();
                    ByteBuf byteBuf = Unpooled.buffer();
                    try {
                        byteBuf.writeByte(104).writeByte(17).writeByte(17).writeByte(104)
                                .writeByte(c).writeShortLE(frame.getClientName())
                                .writeByte(103).writeByte(1).writeByte(6).writeShortLE(frame.getClientName())
                                .writeByte(0).writeByte(0).writeBytes(time).writeByte(0).writeByte(22);
                        byte[] bytes = new byte[byteBuf.writerIndex()];

                        byteBuf.readBytes(bytes);
                        bytes[bytes.length - 2] = (byte) MessageUtil.createCS(bytes, 4, bytes.length - 2);
                        ctx.writeAndFlush(bytes);
                        log.debug("客户端{}:{}，主站请求报文<时钟同步>：{}", clientName, port, Arrays16.toString(bytes));

                        frame.setUpServerFcb(true);
                        frame.setProcess(103);
                        //  备份发送的请求报文
                        frame.setSendingFrame(bytes);

                    } finally {
                        byteBuf.release();
                    }
                } else if (seviceFrame instanceof UploadServiceFrame) {
                    //  标志数据上传完成
//                    frame.setFinished(true);
                    // 终端主动上传遥测数据
                    List<Yc> list = null;
                    UploadServiceFrame uploadServiceFrame = (UploadServiceFrame) seviceFrame;
                    long invokeTime = System.currentTimeMillis();

                    // 1.主动上传 文件字节流
                    ByteBuf byteBuf = frame.getByteBuf();
                    int writerIndex = byteBuf.writerIndex();
                    int readerIndex = byteBuf.readerIndex();

                    if (writerIndex - readerIndex > 0) {
                        byte[] temp = new byte[writerIndex - readerIndex];
                        //  将接收的数据写入临时数组
                        byteBuf.readBytes(temp);
                        //  包装数组
                        ByteBuf fileBytes = Unpooled.wrappedBuffer(temp);

                        //  将接收到的数据写入文件
                        log.debug("客户端{}:{},开始写入当前终端上传文件-----------------------------------------", clientName, port);
                        //  记录原始数据的文件名
                        String fileName = dataPath + clientName + "_" + port + "_" + LocalDateTime.now().toString().replaceAll(":", "-");
                        RandomAccessFile randomAccessFile = null;
                        try {
                            //  打开文件
                            randomAccessFile = new RandomAccessFile(fileName, "rw");
                            //  将接收的数据写入到文件
                            fileBytes.readBytes(randomAccessFile.getChannel(), 0, writerIndex - readerIndex);

                        } catch (Exception e) {
                            log.error("客户端{}:{},file write error:{}", clientName, port, e);
                        } finally {
                            byteBuf.clear();
                            if (randomAccessFile != null) {
                                try {
                                    randomAccessFile.close();
                                } catch (IOException e) {
                                    log.error("客户端{}:{},文件：{}关闭异常", clientName, port, fileName);
                                }
                            }
                        }

                        //  2.将数据写入数据库


                        //  检查是否需要解密
                        boolean flag = false;
                        flag = dqfsDao.hasEncrypted(frame.getClientId());

                        Cipher cipher = null;

                        //  计算本次上传了多少组 '完整的数据'；(一组数据代表:每个时刻的金属腐蚀电流相关的遥测数据)
                        int group = 0;
                        //  检查需要循环的轮数（或读取的行数）
                        if (flag) { //  如果需要解密
                            log.info("客户端{}:{},终端需要解密", clientName, port);
                            group = temp.length / 320;
                        } else {    //  如果不加密
                            group = temp.length / 300;
                        }
                        //  对字节数组添加一个‘具有处理字节数组的方法’的壳
                        ByteBuf byteBuf1 = Unpooled.wrappedBuffer(temp);

                        //  初始化存放 ‘结构化的、将要写入数据库的数据’ 的数据结构
                        list = new ArrayList<>(200);
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
                        SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

                        for (int i = 0; i < group; i++) {
                            String line = null;
                            //  读取行数据
                            if (flag) { //  如果需要解密;此时行数据是320字节;分为176字节和144字节分别解密;
                                try {
                                    //  检查是否初始化解密组件
                                    if (cipher == null) {
                                        //  初始化解密
                                        cipher = Cipher.getInstance("AES/ECB/NoPadding");
                                        //  密码
                                        byte[] secKey = "wwwlinkotechcom.".getBytes();
                                        SecretKeySpec secretKeySpec = new SecretKeySpec(secKey, "AES");
                                        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
                                    }
                                    //  进行解密
                                    byte[] firstArray = new byte[176];
                                    byteBuf1.readBytes(firstArray);
                                    log.info("客户端{}:{}，加密的字节:{}", clientName, port, Arrays16.toString(firstArray));
                                    byte[] original1 = cipher.doFinal(firstArray);
                                    log.info("客户端{}:{}，被解密的字节:{}", clientName, port, Arrays16.toString(original1));
                                    String strOriginal1 = new String(original1, "utf-8");
                                    log.info("客户端{}:{}，被解密的字符串:{}", clientName, port, strOriginal1);

                                    byte[] secondArray = new byte[144];
                                    byteBuf1.readBytes(secondArray);
                                    log.info("客户端{}:{}，加密的字节:{}", clientName, port, Arrays16.toString(secondArray));
                                    byte[] original2 = cipher.doFinal(secondArray);
                                    log.info("客户端{}:{}，被解密的字节:{}", clientName, port, Arrays16.toString(original2));
                                    String strOriginal2 = new String(original2, "utf-8");
                                    log.info("客户端{}:{}，被解密的字符串:{}", clientName, port, strOriginal2);


                                    line = strOriginal1 + strOriginal2;
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }

                            } else {    //  如果不需要解密,直接读取；此时要判断行数据的换行符位置
                                int index = byteBuf1.bytesBefore((byte) 10);
                                //  如果未找到换行符，则退出读取行数据
                                if (index <= 0){ break; }
                                byte[] lineArray = new byte[index];
                                byteBuf1.readBytes(lineArray);
                                line = new String(lineArray);
                                //  读掉行尾的换行符
                                byteBuf1.readByte();
                            }

                            log.debug("客户端{}:{}，读取文件行:{}", clientName, port, line);

                            //  检查读取的一行数据
                            if (line == null || !line.startsWith("16")) {
                                log.debug("客户端{}:{}，文件数据行异常:文件行为null或者不是‘16’开头", clientName, port);
                                continue;
                            }


                            line = line.replace("\n", "").trim();

                            if (line.lastIndexOf("16,2") > 1) {
                                line = line.substring(line.lastIndexOf("16,2"));
                            }

                            String[] datas = line.split(",");

                            //  检查数据完整性，一组数据应该是34个字段
                            if (datas.length != 34) {
                                continue;
                            }

                            //  将解析的数据，结构化到list
                            try {
                                list.add(Yc.builder().
                                        cjsj(sdf.format(sdf2.parse(datas[1]))).
                                        tyndy(MessageUtil.upNanVal(datas[3].trim())).
                                        dcdy(MessageUtil.upNanVal(datas[5].trim())).
                                        jxwd(MessageUtil.upNanVal(datas[7].trim())).
                                        jxsd(MessageUtil.upNanVal(datas[9].trim())).
                                        gprs(MessageUtil.upNanVal(datas[11].trim())).
                                        hjwd(MessageUtil.upNanVal(datas[13].trim())).
                                        hjsd(MessageUtil.upNanVal(datas[15].trim())).
                                        sond(MessageUtil.upNanVal(datas[17].trim())).
                                        yf(MessageUtil.upNanVal(datas[19].trim())).
                                        by1(MessageUtil.upNanVal(datas[21].trim())).
                                        by2(MessageUtil.upNanVal(datas[23].trim())).
                                        by3(MessageUtil.upNanVal(datas[25].trim())).
                                        zndl(MessageUtil.upNanVal(datas[27].trim())).
                                        cudl(MessageUtil.upNanVal(datas[29].trim())).
                                        aldl(MessageUtil.upNanVal(datas[31].trim())).
                                        fedl(MessageUtil.upNanVal(datas[33].trim())).
                                        build()
                                );
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        //  清理ByteBuff
                        byteBuf1.clear();
                        byteBuf1.release();

                    }
                    log.debug("客户端{}:{}，主动文件上传写入读取操作耗时:{},写入大小:{}", clientName, port, (System.currentTimeMillis() - invokeTime), writerIndex);
                    if (uploadServiceFrame.isReleaseFlag()) {
                        // 客户端处于断开状态 需要对bytebuf做清理操作
                        byteBuf.release();
                    } else {

                        if (list != null && list.size() > 0) {
                            // TODO 只能是处理完昨天数据的上传 才可以做升级 或 修改参数操作
                            if (list.get(0).getCjsj().
                                    startsWith(LocalDateTime.now().minusDays(1).
                                            format(DateTimeFormatter.ofPattern("yyyyMMdd")))) {
                                // 执行升级操作
                                // 获取当前版本号
                                byte[] readArgFrame = MessageUtil.createReadArgFrame(frame, (byte) 5, (byte) 128);
                                // 读取终端当前版本号 入队 等待异步发送
                                //  TODO 确认此处是否要优化
//                                sleepReadArgCommon.push(SleepReadArgCommon.ReadArgCls.builder().ctx(ctx).frame(frame).time(new java.util.Date().getTime()).readArgFrame(readArgFrame).build());
                                ctx.writeAndFlush(readArgFrame);
                                log.debug("客户端{}:{}，主站请求报文<读取版本号>:{}", clientName, port, Arrays16.toString(readArgFrame));

                                //  读参数
                                frame.setProcess(202);
                                frame.setUpServerFcb(true);
                                //  备份发送的请求报文
                                frame.setSendingFrame(readArgFrame);
                            }
                        }
                    }

                    if (list != null) {
                        final List<Yc> ycList = list;
                        dataPools.submit(new Runnable() {
                            @Override
                            public void run() {
                                // 批量入库
                                dqfsDao.saveYcList(clientName, port, frame.getClientId(), ycList);
                                dqfsDao.updateIacmDownTime(frame.getClientId());
                            }
                        });

                    }
                } else if (seviceFrame instanceof UpgradePackageServiceFrame) {
                    UpgradePackageServiceFrame upgradePackageServiceFrame = (UpgradePackageServiceFrame) seviceFrame;
                    int type = upgradePackageServiceFrame.getType();
                    if (type == 0) { // 写文件激活
                        byte[] writeActivateFrame = MessageUtil.createWirteActivateFrame(frame, upgradePackageCache.getFileName(), upgradePackageCache.getPackageSize());
                        ctx.writeAndFlush(writeActivateFrame);
                        log.debug("客户端{}:{}，主站请求报文<请求写文件激活指令>:{}", clientName, port, Arrays16.toString(writeActivateFrame));

                        frame.setProcess(210);
                        frame.setUpServerFcb(true);
                        //  备份主站发送的请求报文
                        frame.setSendingFrame(writeActivateFrame);

                    } else if (type == 1) {     //  写文件过程中
                        log.info("客户端{}:{},线程池收到写文件信号", clientName, port);
                        byte[] packageBytes = MessageUtil.createUpgradePackageFrame(frame, upgradePackageCache);
                        log.debug("客户端{}:{},主站请求报文<下发写文件数据>:{}", clientName, port, Arrays16.toString(packageBytes));
                        ctx.writeAndFlush(packageBytes);

                        frame.setProcess(2100);
                        frame.setUpServerFcb(true);
                        //  备份主站发送的请求报文
                        frame.setSendingFrame(packageBytes);
                    }

                } else if (seviceFrame instanceof ResetServiceFrame) {
                    byte[] resetFrame = MessageUtil.createResetFrame(frame);
                    ctx.writeAndFlush(resetFrame);
                    log.debug("客户端{}:{}，主站请求报文<终端复位进程指令>:{}", clientName, port, Arrays16.toString(resetFrame));

                    frame.setProcess(105);
                    frame.setUpServerFcb(true);
                    // 备份主站发送的请求报文
                    frame.setSendingFrame(resetFrame);

                } else if (seviceFrame instanceof UpConnectArgServiceFrame) {
                    // ip port 修改
                    ConnectArg connectArg = dqfsDao.getConnectArg(frame.getClientId());
                    if (connectArg != null) {
                        log.debug("客户端{}:{}，参数修改开始执行",clientName, port);
                        byte[] argFrame = MessageUtil.createArgFrame(frame, connectArg);
                        ctx.writeAndFlush(argFrame);

                        //  备份主站的请求报文
                        frame.setSendingFrame(argFrame);

                        frame.setArgId(connectArg.getArgId());
                        log.debug("客户端{}:{}，服务端请求参数预置,当前index：{},报文:{}", clientName, port, frame.getUpgradePackageindex(), Arrays16.toString(argFrame));
                    }

                } else if (seviceFrame instanceof ArgSolidifyServiceFrame) {
                    byte[] bytes = MessageUtil.createArgSolidifyFrame(frame);
                    ctx.writeAndFlush(bytes);
                    log.debug("客户端{}:{}，主站请求报文<参数固化指令>:{}", clientName, port, Arrays16.toString(bytes));

                    //  备份主站发送的请求报文
                    frame.setSendingFrame(bytes);

                }
            }
        });
    }


}
